extern crate serde_json;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::str;
use std::sync::Mutex;

use super::check::SystemInfoAndBuild;
use super::routes::Parameter;
use super::utils::load_uuid_output;
use super::utils::OutErr;
use super::utils::StringSplit;
use super::utils::list_brd_uuid_output;
// use super::utils::OutputObject;
use std::marker::Send;

#[derive(Default)]
pub struct TmpData {}
pub struct Nodes;

lazy_static! {
    static ref ALIVE: Mutex<HashMap<&'static str, String>> = {
        // let mut map = HashMap::new();
        let map = list_brd_uuid_output();
        Mutex::new(map)
    };
}

impl Nodes {
    pub fn get(k: &str) -> String {
        let map = ALIVE.lock().unwrap();
        let mut out_vec: String = String::from("");
        for (a, b) in map.iter() {
            if *a == k {
                out_vec = b.to_string();
                break;
            }
        }
        out_vec
    }
    pub fn node(k: &str, status: &str) -> OutErr<i64> {
        let mut map = ALIVE.lock().unwrap();
        map.insert(string_static(k.to_string()), status.to_string());
        Ok(map.len() as i64)
    }

    pub fn remove_node(k: &str) -> OutErr<i64> {
        let mut map = ALIVE.lock().unwrap();
        map.remove(string_static(k.to_string()));
        Ok(map.len() as i64)
    }
    pub fn get_task_ids(key: &str) -> Vec<String> {
        let map = ALIVE.lock().unwrap();
        let mut out_vec: Vec<String> = vec![];
        let search_key = format!("|| {} ||", key);
        let _ = map
            .iter()
            .map(|(a, _)| {
                if a.contains(&search_key) {
                    let (host, _, send_id) = a.split_twice(" || ");
                    out_vec.push(format!("{} || {}", send_id, host));
                }
            })
            .collect::<Vec<()>>();
        out_vec
    }
    pub fn nodes() -> Vec<String> {
        let map = ALIVE.lock().unwrap();
        let mut out_vec: Vec<String> = vec![];
        let _ = map
            .iter()
            .map(|(a, _)| {
                out_vec.push(a.to_string());
            })
            .collect::<Vec<()>>();
        out_vec
    }

    pub fn nodes_filter(f: &str) -> Vec<String> {
        let map = ALIVE.lock().unwrap();
        let mut out_vec: Vec<String> = vec![];
        let _ = map
            .iter()
            .map(|(a, b)| {
                if b == f {
                    out_vec.push(a.to_string());
                }
            })
            .collect::<Vec<()>>();
        out_vec
    }

    pub fn status() -> String {
        let mut out = String::new();
        let map = ALIVE.lock().unwrap();
        let _ = map
            .iter()
            .map(|(a, b)| {
                out.push_str(&format!("{} : {}\n", a, b));
            })
            .collect::<Vec<()>>();
        out
    }
}

lazy_static! {
    static ref HASHMAP: Mutex<HashMap<&'static str, Value>> = {
        let map = load_uuid_output();
        Mutex::new(map)
    };
}

pub trait DB: Send {
    fn contains_key(&self, key_str: &str) -> bool;
    fn query(&self, query_str: &str) -> Value;
    fn fuzzy(&self, query_str: &str) -> Vec<Value>;

    fn insert(&self, key_str: &str, val: Value) -> i64;
    fn insert_from_json(&self, key_str: &str, val: &str) -> OutErr<i64>;
    fn remove(&self, key_str: &str) -> i64;
    fn update(&self, key_str: &str, val: Value) -> bool;
    fn keys(&self) -> Vec<String>;

    fn len(&self) -> i64;
}

impl TmpData {
    pub fn keys() -> Vec<String> {
        let map = HASHMAP.lock().unwrap();
        let keys: Vec<String> = map.keys().map(|key| key.to_string()).collect();
        keys
    }

    pub fn fuzzy(query_str: &str) -> Vec<Value> {
        let map = HASHMAP.lock().unwrap();
        let mut results: Vec<Value> = vec![];
        for (key, value) in map.iter() {
            if key.contains(query_str) {
                results.push(value.clone());
            }
        }
        results
    }

    pub fn items(query_str: &str) -> Vec<(String, String)> {
        let mut results: Vec<(String, String)> = vec![];
        let map = HASHMAP.lock().unwrap();
        for (key, value) in map.iter() {
            if key.contains(query_str) {
                results.push((key.to_string(), value.to_string()));
            } else if query_str == "[uuid]" {
                match uuid::Uuid::parse_str(key) {
                    Ok(_) => {
                        results.push((key.to_string(), value.to_string()));
                    }
                    Err(_) => {
                        continue;
                    }
                }
            }
        }
        results
    }

    pub fn query(query_str: &str) -> Value {
        let mut out_value = Value::default();
        let map = HASHMAP.lock().unwrap();
        for (key, value) in map.iter() {
            if *key == query_str {
                out_value = value.clone();
                break;
            }
        }
        out_value
    }

    pub fn clean() {
        for k in TmpData::keys() {
            TmpData::remove(&k);
        }
    }

    pub fn remove(key_str: &str) -> i64 {
        let mut map = HASHMAP.lock().unwrap();
        map.remove(string_static(key_str.to_string()));
        map.len() as i64
    }

    pub fn insert(key_str: &str, val: Value) -> i64 {
        let mut map = HASHMAP.lock().unwrap();
        map.insert(string_static(key_str.to_string()), val);
        map.len() as i64
    }

    fn insert_from_json(key_str: &str, val_str: &str) -> OutErr<i64> {
        let val = serde_json::json!(val_str);
        Ok(TmpData::insert(key_str, val))
    }

    fn len() -> i64 {
        let map = HASHMAP.lock().unwrap();
        map.len() as i64
    }
}

fn string_static(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

pub fn db_protocol(para: &Parameter, _: &SystemInfoAndBuild) -> (i32, String) {
    let help = r#"
ls  -> ls all keys sep by ","
gt  -> get value from tmpdb;
sr  -> search fuzzy get value from tmpdb;
up  -> insert / update value in 'data' by filed in 'data_tp'  
rm  -> rm value in db.
co  -> count values in db.

    "#;
    let (op, data, tp) = para.get_args_str();
    let code: i32;
    // let mut msg = String::from(help);
    let msg = match op {
        "ls" => {
            code = 0;
            TmpData::keys().join(",")
        }
        "gt" => {
            code = 0;
            TmpData::query(data).to_string()
        }
        "sr" => {
            code = 0;
            let arr: Value = TmpData::fuzzy(data).into();
            arr.to_string()
        }
        "co" => {
            code = 0;
            json!({ "count": TmpData::len() }).to_string()
        }
        "up" => match TmpData::insert_from_json(tp, data) {
            Ok(n) => {
                code = 0;
                json!({ "count": n }).to_string()
            }
            Err(e) => {
                code = 2;
                json!({
                    "count": -1,
                    "reason" : e.to_string()
                })
                .to_string()
            }
        },
        "rm" => {
            code = 0;
            TmpData::remove(data).to_string()
        }
        _ => {
            code = 1;
            help.to_string()
        }
    };
    (code, msg)
}
